package com.ys.util;

import com.bo.util.DateUtil;
import com.ys.config.MyConfig;
import com.ys.entity.sys.YsGridCol;
import com.ys.entity.sys.YsGrid;
import com.ys.entity.sys.YsGridCol;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 导出Excel 明细使用
 * @param <T>
 * @author DH-Xxj
 * @version 2019年11月27日
 */
public class ExportMxExcelUtils<T>{
    
    private static final Logger log = LoggerFactory.getLogger(ExportMxExcelUtils.class);

    /**
     * Excel sheet最大行数，默认100000
     */
    public static final int sheetSize = 100000;

    /**
     * 工作表名称
     */
    private String sheetName;

    /**
     * 工作薄对象
     */
//    private Workbook wb;
//    private HSSFWorkbook wb;	//office 2003 及以下版本【.xls】
//    private XSSFWorkbook wb;	//office 2007 及以上版本【.xlsx】
    private SXSSFWorkbook wb;	//office 2007 及以上版本【.xlsx】

    /**
     * 工作表对象
     */
    private Sheet sheet;

    /**
     * 样式列表
     */
    private Map<String, CellStyle> styles;
    
    /**
     * 导出的字段列表
     */
    private List<YsGridCol> listCol;
    
    private List<Map<String, Object>> listData;

    public T entity;

    /**
     * 对应实体对象
     */
    public Class<T> clazz;
    
    /**
     * 构造一个导出对象
     * @param clazz 对应实体对象
     */
    @SuppressWarnings("unchecked")
	public ExportMxExcelUtils(T entity){
    	this.entity = entity;
    	this.clazz = (Class<T>) entity.getClass();
    }

    /**
     * 将list中的数据导入到excel表单
     * @param listData 导出的数据列表
     * @param listCol 导出的字段列表
     * @param sheetName 工作表的名称
     * @param type 导出类型（export:导出数据；template：导入模板）
     * @return AjaxResult
     * @throws Exception 
     */
    public String exportExcel(List<Map<String, Object>> listData, List<YsGridCol> listCol, String sheetName) throws Exception{
        this.listCol = listCol;
        this.sheetName = sheetName;
        this.listData = listData;
    	//创建一个工作簿
        this.wb = new SXSSFWorkbook(100); // keep 100 rows in memory, exceeding rows will be flushed to disk
//        this.wb = new XSSFWorkbook();
        return exportExcel();
    }

    /**
     * 将创建好的excel导出生成文件
     * @return AjaxResult
     * @throws Exception 
     */
    public String exportExcel() throws Exception{
        OutputStream out = null;
        try{
            // 取出一共有多少个sheet.
            double sheetNo = Math.ceil(listData.size() / sheetSize);
            for (int index = 0; index <= sheetNo; index++){
                createSheet(sheetNo, index);

                // 产生一行
                Row row = sheet.createRow(0);
                int column = 0;
                // 写入各个字段的列头名称
                for (YsGridCol exportField : listCol){
                    this.createCell(exportField, row, column++);
                }
                // 填充数据
                fillExcelData(index, row);
            }
            String filename = sheetName + "_" + (DateUtil.getYYYY_MM_DD_HH_mm_ss(new Date()).replace("-", "").replace(" ", "").replace(":", "")) + ".xlsx";
            
            String toFilePath = getAbsoluteFile(filename);
            out = new FileOutputStream(toFilePath);
            wb.write(out);
            return toFilePath;
        }
        catch (Exception e){
        	e.printStackTrace();
            log.error("导出Excel异常{}", e.getMessage());
            throw new Exception("导出Excel失败，请联系网站管理员！");
        }
        finally{
            if (wb != null){
                try{
                    wb.close();
                }
                catch (IOException e1){
                    e1.printStackTrace();
                }
            }
            if (out != null){
                try{
                    out.close();
                }
                catch (IOException e1){
                    e1.printStackTrace();
                }
            }
        }
    }

    /**
     * 创建工作表 
     * @param sheetNo sheet数量
     * @param index 序号
     */
    public void createSheet(double sheetNo, int index){
        this.sheet = wb.createSheet();
        this.styles = createStyles(wb);
        // 设置工作表的名称
        if (sheetNo == 0){
            wb.setSheetName(index, sheetName);
        }
        else{
            wb.setSheetName(index, sheetName + index);
        }
    }

    /**
     * 创建单元格
     */
    public Cell createCell(YsGridCol exportField, Row row, int column){
        // 创建列
        Cell cell = row.createCell(column);
        //设置列宽度，第一个参数代表列id(从0开始),第2个参数代表宽度值
    	sheet.setColumnWidth(column, 3766); 
        // 写入列信息
        cell.setCellValue(exportField.getName());
//        setDataValidation(attr, row, column);
        cell.setCellStyle(styles.get("header"));
        return cell;
    }

    /**
     * 填充excel数据
     * @param index 序号
     * @param row 单元格行
     */
    public void fillExcelData(int index, Row row){
    	/*
    	 * 单元格样式的最大数目64000，若在循环内每个单元格创建样式（createDataFormat），数据量大时将超出。
    	 * 因此在循环外预定相关样式，以便选择使用，而不是每次都创建新样式。
    	 */
    	CellStyle cellStyle = wb.createCellStyle();		//预设样式
    	cellStyle.cloneStyleFrom(styles.get("data"));	//预设样式
    	
    	int startNo = index * sheetSize;
        int endNo = Math.min(startNo + sheetSize, listData.size());
        for (int i = startNo; i < endNo; i++){
            row = sheet.createRow(i + 1 - startNo);
            // 得到导出对象
            Map<String, Object> dataMap = listData.get(i);
            int column = 0;
            for (YsGridCol exportField : listCol){
            	String cellValueStr = "";
            	Object cellValue = dataMap.get(exportField.getCode());
                if (cellValue != null){
            		cellValueStr = cellValue.toString();
                }
                
                Cell cell = row.createCell(column);
                cell.setCellStyle(cellStyle);  //设置单元格样式
            	cell.setCellValue(cellValueStr);

                column++;
            }
        }
    }
    
    /**
     * 创建表格样式
     * @param wb 工作薄对象
     * @return 样式列表
     */
    private Map<String, CellStyle> createStyles(SXSSFWorkbook wb){
        // 写入各条记录,每条记录对应excel表中的一行
        Map<String, CellStyle> styles = new HashMap<String, CellStyle>();
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.LEFT);	//设置单元格的水平对齐类型（居左）
        style.setVerticalAlignment(VerticalAlignment.CENTER);	//设置单元格的垂直对齐类型（居中）
        style.setBorderRight(BorderStyle.THIN);	//设置单元格右边框的边框类型
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());	//设置右边边框的颜色
        style.setBorderLeft(BorderStyle.THIN);	//设置单元格左边框的边框类型
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());	//设置左边框的颜色
        style.setBorderTop(BorderStyle.THIN);	//设置单元格上边框使用的边框类型
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());	//设置上边框的颜色
        style.setBorderBottom(BorderStyle.THIN);	//设置单元格下边框使用的边框类型
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());	//设置下边框的颜色
        Font dataFont = wb.createFont();
//      headerFont.setFontName("Arial");	//设置字体的名称(例如:Arial)
        dataFont.setFontName("仿宋");	//设置字体的名称(例如:仿宋)
        dataFont.setFontHeightInPoints((short) 12);	//设置字体高度
        style.setFont(dataFont);
        styles.put("data", style);

        style = wb.createCellStyle();
        style.cloneStyleFrom(styles.get("data"));
        style.setAlignment(HorizontalAlignment.CENTER);	//设置单元格的水平对齐类型（居中）
        style.setVerticalAlignment(VerticalAlignment.CENTER);	//设置单元格的垂直对齐类型（居中）
        style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());	//
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        Font headerFont = wb.createFont();
//        headerFont.setFontName("Arial");	//设置字体的名称(例如:Arial)
        dataFont.setFontName("仿宋");	//设置字体的名称(例如:仿宋)
        headerFont.setFontHeightInPoints((short) 12);	//设置字体高度
        headerFont.setBold(true);
        headerFont.setColor(IndexedColors.WHITE.getIndex());
        style.setFont(headerFont);
        styles.put("header", style);

        return styles;
    }

    /**
     * 获取下载路径
     * 
     * @param filename 文件名称
     */
    public String getAbsoluteFile(String filename)
    {
        String downloadPath = MyConfig.getProfile() + "/" + filename;
        File desc = new File(downloadPath);
        if (!desc.getParentFile().exists())
        {
            desc.getParentFile().mkdirs();
        }
        return downloadPath;
    	 
    }

}
